package ginv

import (
	"context"
	"gitee.com/filters/logger"
	"gitee.com/filters/net-plug/refer"
	"gitee.com/filters/reader/wviper"
	"github.com/gin-gonic/gin"
	"github.com/robfig/cron/v3"
	"github.com/rs/zerolog/log"
	"net/http"
	"os"
	"os/signal"
	"reflect"
	"strconv"
	"syscall"
	"time"
)

const GinConfig = "Ginserver"

var (
	configData *wviper.Reader
	ginSrv     *gin.Engine
)

type Starter struct {
	writer    *wviper.Reader
	isOpenLog bool
	es        *gin.Engine
	err       error
}

// CronTabObj ...
type CronTabObj struct {
	Name    string
	CronTab string
	Handler func() error
}

type InFunc func(keys reflect.Value, val any) error

// New 初始化结构体
func New(options ...wviper.Option) *Starter {
	config, err := wviper.NewSetting(options...)
	if err != nil {
		panic(err)
	}
	configData = config
	ginSrv = gin.Default()
	return &Starter{
		writer: config,
		es:     ginSrv,
	}
}

// UseLog 日志记录
func (s *Starter) UseLog() *Starter {
	s.isOpenLog = true
	s.err = logger.NewLog(logger.WithWritePath(s.writer.GetStringMapString(logger.Logger)[logger.LogPathName]),
		logger.WithLogExt(s.writer.GetStringMapString(logger.Logger)[logger.LogExtName])).CurLog().WrapFormat()
	return s
}

// BindField 绑定数据
func (s *Starter) BindField(f ...interface{}) *Starter {
	for _, i2 := range f {
		t2 := reflect.TypeOf(i2).Elem()
		v2 := reflect.ValueOf(i2).Elem()
		for i := 0; i < v2.NumField(); i++ {
			InParamMapFunc(t2.Field(i).Type.Elem().String())(v2.Field(i), s.writer.Bind(t2.Field(i).Name))
		}
	}
	return s
}

// HandleRouter 绑定路由
func (s *Starter) HandleRouter(route func(r *gin.Engine)) *Starter {
	route(s.es)
	return s
}

// HandleAddFunc 定时执行函数
func (s *Starter) HandleAddFunc(list []*CronTabObj) *Starter {
	c := cron.New(cron.WithSeconds())
	for index, crontabObj := range list {
		func(id int, obj *CronTabObj) {
			_, _ = c.AddJob(
				obj.CronTab, //一分钟
				cron.FuncJob(func() {
					_ = obj.Handler()
				}))
		}(index, crontabObj)
	}
	c.Start() //启动crond
	return s
}

// HandleMiddleware 绑定中间件
func (s *Starter) HandleMiddleware(middles ...gin.HandlerFunc) *Starter {
	s.es.Use(middles...)
	return s
}

// GetConfig 获取配置信息
func (s *Starter) GetConfig() *wviper.Reader {
	return s.writer
}

// GetSrv 获取gin的服务
func (s *Starter) GetSrv() *gin.Engine {
	return s.es
}

// GetConfig ...
func GetConfig() (*wviper.Reader, error) {
	if configData == nil {
		return configData, refer.ErrorNotFoundConfigInfo
	}
	return configData, nil
}

// GetGinConfig ...
func GetGinConfig() any {
	return configData.Get(GinConfig)
}

// GetSrv ...
func GetSrv() (*gin.Engine, error) {
	if ginSrv == nil {
		return ginSrv, refer.ErrorNotFoundGinSrv
	}
	return ginSrv, nil
}

// SampleRun 简单启动，非平滑启动
func (s *Starter) SampleRun() error {
	if s.err != nil {
		return s.err
	}
	val := s.writer.GetStringMap(GinConfig)
	gin.SetMode(val["run_mode"].(string))
	return s.es.Run(":" + strconv.Itoa(val["http_port"].(int)))
}

// Run 启动
func (s *Starter) Run() error {
	if s.err != nil {
		return s.err
	}
	val := s.writer.GetStringMap(GinConfig)
	gin.SetMode(val["run_mode"].(string))
	srv := &http.Server{
		Addr: ":" + strconv.Itoa(val["http_port"].(int)),
		Handler:s.es,
	}
	go func() {
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Err(err).Msg("Run err")
		}
	}()
	// wait for interrupt signal to gracefully shutdown the server with
	// a timeout of 10 seconds.
	quit := make(chan os.Signal, 1)
	// kill (no param) default send syscall.SIGTERM
	// kill -2 is syscall.SIGINT
	// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	log.Info().Msg("Shutdown Server ...")
	ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
	defer cancel()
	if err:= srv.Shutdown(ctx); err != nil {
		log.Err(err).Msg("Server Shutdown ...")
	}
	log.Info().Msg("Server exiting")
	return nil
}
